""" S3D.Net package """
import io
import struct
import pickle
import queue

class Packer:
    def __init__(self, stream = None):
        self.buffer = stream or io.BytesIO()

    def writeInt(self, value):
        self.buffer.write(struct.pack("<i", value))

    def writeShort(self, value):
        self.buffer.write(struct.pack("<h", value))

    def writeStr(self, value):
        data = value.encode()
        self.writeShort(len(data))
        self.buffer.write(data)

    def writeObj(self, value):
        data = io.BytesIO()
        pickle.dump(value, data)
        data = data.getvalue()

        self.writeShort(len(data))
        self.buffer.write(data)

    def getValue(self):
        return self.buffer.getvalue()

class Unpacker:
    def __init__(self, input):
        self.input = input

    def readInt(self):
        data = self.input.read(4)
        res, = struct.unpack("<i", data)
        return res

    def readShort(self):
        data = self.input.read(2)
        res, = struct.unpack("<h", data)
        return res

    def readStr(self):
        length = self.readShort()
        if length == 0:
            return ""

        data   = self.input.read(length)
        return data.decode()

    def readObj(self):
        length = self.readShort()
        data   = self.input.read(length)
        return pickle.loads(data)

    def readData(self, size):
        return self.input.read(size)

    def getStream(self):
        return self.input

class ReadBuffer:
    def __init__(self):
        self.data   = queue.deque([])
        self.offset = 0
        self.size   = 0

    def append(self, buf):
        self.data.append(buf)
        self.size += len(buf)

    def getSize(self):
        return self.size - self.offset

    def readAll(self):
        return self.read(self.getSize())

    def read(self, count):
        assert count <= self.getSize()

        res = io.BytesIO()

        while count > 0:
            cur = self.data[0]

            if count < len(cur) - self.offset:
                res.write(cur[self.offset : self.offset + count])
                self.offset += count
                count = 0
            else:
                res.write(cur[self.offset:])
                count -= len(cur) - self.offset
                self.data.popleft()

                self.size   -= len(cur)
                self.offset = 0

        return res.getvalue()